namespace PaulRz.Util.Patterns
{
    public abstract class SyncTerminatorPipelineElement<TInput, TOutput> : TerminatorPipelineElement<TInput, TOutput>
        where TInput : class
        where TOutput : class 
    {
        protected SyncTerminatorPipelineElement(int numOfThreads, string name, ILogInterface logger, int joinTime) : base(name, logger, joinTime)
        {
            consumers = new SyncConsumer[numOfThreads];
            producers = new ProducerPipelineElement<TOutput>[1];
            producers[0] = new SimpleProducer<TOutput>(name, logger, joinTime);

            for(int i=0; i<numOfThreads; ++i)
            {
                consumers[i] = new SyncConsumer(this, name+i, logger, joinTime);
            }
        }

        /// <summary>
        /// Process input and returns output
        /// </summary>
        /// <remarks>Method should be thread-safe!</remarks>
        /// <param name="input"></param>
        /// <returns></returns>
        protected abstract TOutput ProcessSync(TInput input);

        protected override void Run()
        {
        }

        protected override void ProcessCycle()
        {
        }

        private sealed class SyncConsumer : ConsumerPipelineElement<TInput>
        {
            private SyncTerminatorPipelineElement<TInput, TOutput> parent;
            
            public SyncConsumer(SyncTerminatorPipelineElement<TInput, TOutput> parent, string name, ILogInterface logger, int joinTime) : base(name, logger, joinTime)
            {
                this.parent = parent;
            }

            protected override void ProcessInput(TInput message)
            {
                if(message==null)
                {
                    Logger.LogError("Null message in the SyncConsumer.ProcessInput!");
                    return;
                }

                Logger.LogEvent("Message {0} is ready for sending", message);
                TOutput result = parent.ProcessSync(message);

                if (result != null)
                {
                    //parent.Output.Enqueue(result);
                    parent.producers[0].Output.Enqueue(result);
                    Logger.LogEvent("Message {0} is sent", message);
                }
                else
                {
                    Logger.LogError("Null message generated by SyncConsumer.ProcessSync!");
                }
            }
        }
    }
}
